import { getValue } from "../util/ObjectUtil.js";
import { rebuild } from "./mount.js";
import { renderData, renderNode } from "./render.js";

/**
 *  对传入的对象进行代理，返回一个代理对象，
 *  通过这个代理对象来获取和设置对象的属性值,在设置获取过程中可以提供额外的服务
 *  把直接的对象（没有对象嵌套）的key和value绑定到vm的属性上。
 * @param {该Due对象} vm
 * @param {要代理的对象} obj
 * @param {命名空间} namespace 表示当前属性是怎么被调用的，第一个传入的对象没有被指向的key，所以为""。后面render渲染数据要用
 * @returns 代理对象
 */
export function constructProxy(vm, obj, namespace) {
  let proxyObj = null;
  if (obj instanceof Array) {
    proxyObj = new Array(obj.length);
    for (let i = 0; i < proxyObj.length; i++) {
      if (obj[i] instanceof Object) {
        proxyObj[i] = constructProxy(vm, obj[i], namespace);
      } else {
        proxyObj[i] = obj[i];
      }
    }
    proxyObj = proxyArr(vm, proxyObj, namespace);
  } else if (obj instanceof Object) {
    proxyObj = constructObjectProxy(vm, obj, namespace);
  } else {
    throw new Error("error");
  }
  return proxyObj;
}

//给纯对象构建代理
function constructObjectProxy(vm, obj, namespace) {
  let proxyObj = {};

  for (let prop in obj) {
    //es6属性描述符
    Object.defineProperty(proxyObj, prop, {
      configurable: true,
      get() {
        return obj[prop];
      },
      set(value) {
        obj[prop] = value;
        let name = getNameSpace(namespace, prop);  
        let vnodes = renderData(vm, name);
        
        if (vnodes) {
          for (let i = 0; i < vnodes.length; i++) {
            renderNode(vm, vnodes[i]);
          }
        }
      },
    });

    //给vm实例对象挂载vue-data属性
    if (!namespace) {
      Object.defineProperty(vm, prop, {
        configurable: true,
        get() {
          return obj[prop];
        },
        set(value) {
          obj[prop] = value;
          let name = getNameSpace(namespace, prop);
          let val = getValue(vm._data,name); //获得vue-data-value
          if (val instanceof Array) {  //如果是数组，则可能会被v-for循环
            rebuild(vm,name); //所以要设置数组的值时就要重新构建这个结点的虚拟树
          }
          let vnodes = renderData(vm, name);
          if (vnodes) {
            for (let i = 0; i < vnodes.length; i++) {
              renderNode(vm, vnodes[i]);
            }
          }
        },
      });
    }

    if (obj[prop] instanceof Object) {
      proxyObj[prop] = constructProxy(
        vm,
        obj[prop],
        getNameSpace(namespace, prop)
      );
    }
  }
  return proxyObj;
}



function proxyArr(vm, arr, namespace) {
  //给Array数组重置他的方法
  let obj = {
    eleType: "Array",
    toString: function (params) {
      let result = "";
      for (let i = 0; i < arr.length; i++) {
        result += arr[i] + ", ";
      }
      return result.substring(0, result.length - 2);
    },
    push() {},
    pop() {},
    shift() {},
    unshift() {},
  };
  defArrayFunc.call(vm, obj, "push", namespace, vm);
  defArrayFunc.call(vm, obj, "pop", namespace, vm);
  defArrayFunc.call(vm, obj, "shift", namespace, vm);
  defArrayFunc.call(vm, obj, "unshift", namespace, vm);
  arr.__proto__ = obj;
  return arr;
}

//给函数的键值是函数进行代理，代理还是调用Array的方法，但是添加了一些操作
//传入vm便于时时刻刻找打他的本体在哪，是一个规范
const arrayProto = Array.prototype;
function defArrayFunc(obj, func, namespace, vm) {
  Object.defineProperty(obj, func, {
    enumerable: true,
    configurable: true,
    value() {
      const original = arrayProto[func];
      const result = original.apply(this, arguments);
      let name = getNameSpace(namespace, "");
      rebuild(vm, name);
      renderData(vm, name);
      return result;
    },
  });
}

function getNameSpace(nowNameSpace, nowProp) {
  if (nowNameSpace == null || nowNameSpace == "") {
    return nowProp;
  } else if (nowProp == null || nowProp == "") {
    return nowNameSpace;
  } else {
    return nowNameSpace + "." + nowProp;
  }
}
